package com.lboyang.remoting.transport.netty.codec;

import com.lboyang.common.enums.CompressTypeEnum;
import com.lboyang.common.enums.SerializationTypeEnum;
import com.lboyang.common.extension.ExtensionLoader;
import com.lboyang.remoting.compress.Compress;
import com.lboyang.common.constants.NrpcConstants;
import com.lboyang.common.dto.NrpcMessage;
import com.lboyang.remoting.serialize.Serializer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Description
 * @Date 2021/9/12 12:09
 * @author: A.iguodala
 */

@Slf4j
public class RpcMessageEncoder extends MessageToByteEncoder<NrpcMessage> {

    /**
     * 生成 Request 唯一 ID
     */
    private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(0);

    /**
     * 编码方法，将消息编码为二进制数据包
     * @param channelHandlerContext
     * @param nrpcMessage
     * @param byteBuf
     * @throws Exception
     */
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, NrpcMessage nrpcMessage, ByteBuf byteBuf) throws Exception {
        try {
            // 写入魔数
            byteBuf.writeBytes(NrpcConstants.MAGIC_NUMBER);
            // 写入版本号
            byteBuf.writeByte(NrpcConstants.VERSION);
            // 预留四个字节的长度用于写【数据包总长度】字段
            byteBuf.writerIndex(byteBuf.writerIndex() + 4);
            // 消息类型
            byte messageType = nrpcMessage.getMessageType();
            byteBuf.writeByte(messageType);
            // 消息编码格式隔离
            byteBuf.writeByte(nrpcMessage.getCodec());
            // 压缩格式类型
            byteBuf.writeByte(CompressTypeEnum.GZIP.getCode());
            // 唯一标识 Request ID
            byteBuf.writeInt(ATOMIC_INTEGER.getAndIncrement());
            // 构造总长度
            int fullLength = NrpcConstants.HEAD_LENGTH;
            byte[] bodyBytes = null;
            // 如果不是心跳检测包，则 【总长度】= 【首部长度】+【实体长度】
            if (messageType != NrpcConstants.HEARTBEAT_REQUEST_TYPE
                    && messageType != NrpcConstants.HEARTBEAT_RESPONSE_TYPE) {
                // 获取序列化协议
                String codecName = SerializationTypeEnum.getName(nrpcMessage.getCodec());
                log.info("codec name: [{}] ", codecName);
                // 通过 SPI 获取对应的序列化器
                Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class)
                        .getExtension(codecName);
                // 得到序列化后的实体数据
                bodyBytes = serializer.serialize(nrpcMessage.getData());
                // 压缩实体内容
                String compressName = CompressTypeEnum.getName(nrpcMessage.getCompress());
                // 通过 SPI 获取压缩器
                Compress compress = ExtensionLoader.getExtensionLoader(Compress.class)
                        .getExtension(compressName);
                // 赋值压缩的实体数据
                bodyBytes = compress.compress(bodyBytes);
                // 计算总长度
                fullLength += bodyBytes.length;
            }
            // 写入实体数据
            if (bodyBytes != null) {
                byteBuf.writeBytes(bodyBytes);
            }
            // 填入总长度字段
            int writeIndex = byteBuf.writerIndex();
            byteBuf.writerIndex(writeIndex - fullLength + NrpcConstants.MAGIC_NUMBER.length + 1);
            byteBuf.writeInt(fullLength);
            byteBuf.writerIndex(writeIndex);
        } catch (Exception e) {
            log.error("Encode request error!", e);
        }
    }
}
